package org.client.iot.ble;

import static org.client.iot.constant.GlobeConstant.BLE_SERVICE.*;

import android.annotation.SuppressLint;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.os.Handler;
import android.util.Log;

import com.clj.fastble.BleManager;
import com.clj.fastble.callback.BleGattCallback;
import com.clj.fastble.callback.BleMtuChangedCallback;
import com.clj.fastble.callback.BleNotifyCallback;
import com.clj.fastble.callback.BleScanCallback;
import com.clj.fastble.callback.BleWriteCallback;
import com.clj.fastble.data.BleDevice;
import com.clj.fastble.data.BleScanState;
import com.clj.fastble.exception.BleException;
import com.clj.fastble.scan.BleScanRuleConfig;

import org.client.iot.constant.GlobeDataManager;
import org.client.iot.util.StringUtils;
import org.tzw.template.logger.LocalLogger;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import cn.hutool.core.util.StrUtil;

public class BleTemplate {
    private UUID mServiceReadUUID, mCharacterReadUUID, mServiceWriteUUID, mCharacterWriteUUID;
    private int mCurrentIndex = 0;
    private List<String> cmdList = new ArrayList<>();
    private Handler mHandler;
    private Handler mSyncHandler;
    private Runnable mRunnable;
    private String mCurrentCmd;
    private BleDevice mDevice;
    private boolean isScan = false;
    private String mMac;

    private BleTemplate() {

    }

    public static BleTemplate getInstance() {
        return DataManagerHolder.sInstance;
    }

    private static class DataManagerHolder {
        private static BleTemplate sInstance = new BleTemplate();
    }


    public void start(BleDevice device) {
        if (device == null) return;
        this.mDevice = device;
        mMac = mDevice.getMac();
        initData();
        initCmds();
        setParseListener();
        registerConnectStatusListener();
        connectDeviceIfNeeded();
    }

    private boolean sendCommand = true;

    public void pauseSendCommand() {
        showLog("暂停");
        this.sendCommand = false;
    }

    public void resumeSendCommand() {
        showLog("开始");
        this.sendCommand = true;
        if (isConnected() && mDevice != null && mHandler != null) {
            mHandler.postDelayed(mRunnable, 500);
        }
    }

    private void initData() {
        if (mHandler == null) {
            mHandler = new Handler();
        }
        if (mSyncHandler == null) {
            mSyncHandler = new Handler();
        }
        mRunnable = new Runnable() {
            @Override
            public void run() {
                if(sendCommand) {
                    writeNextCmd();
                }
                mHandler.postDelayed(this, 2 * 1000);
            }
        };
    }

    private void registerConnectStatusListener() {
//        ClientManager.getClient().registerConnectStatusListener(mDevice.getAddress(), mConnectStatusListener);
    }

    // 断开连接
    public void cancelConnect() {
        if (mDevice != null) {
            BleManager.getInstance().disconnect(mDevice);
            if (mSyncHandler != null) {
                mSyncHandler.removeCallbacksAndMessages(null);
                mSyncHandler = null;
            }
            if (mHandler != null) {
                mHandler.removeCallbacksAndMessages(null);
                mHandler = null;
            }
        }
    }

    /**
     * 初始化命令集
     */
    private void initCmds() {
        cmdList.clear();
//        cmds.add(Cmds.REAL_SOFTWARE_VERSION_REQUEST);
//        cmds.add(Cmds.REAL_HARDWARE_VERSION_REQUEST);
//        cmdList.add(Cmds.BATTERY_STATUS_REQUEST);
//        cmdList.add(Cmds.BATTERY_REQUEST);
//        cmdList.add(Cmds.REAL_READ_STEP_REQUEST);
        mCurrentIndex = 0;
//        mCurrentCmd = cmdList.get(mCurrentIndex);
    }

    /**
     * 设置解析监听
     */
    private void setParseListener() {

    }

    private void writeNextCmd() {
        if(cmdList.size()>0) {
            mCurrentIndex++;
            mCurrentIndex %= cmdList.size();
            if (cmdList.size() > mCurrentIndex && sendCommand) {
                mCurrentCmd = cmdList.get(mCurrentIndex);
                showLog("发送---写入命令writeNextCmd():" + mCurrentCmd);
                write(mCurrentCmd);
            }
        }
    }

    public void write(String mCurrentCmd) {
        if (!isConnected()) {
            showLog("写入指令失败,未连接!");
            return;
        }
        showLog("写入命令:" + mCurrentCmd);
        if (mServiceWriteUUID != null && mCharacterWriteUUID != null) {
            BleManager.getInstance().write(mDevice, mServiceWriteUUID.toString(), mCharacterWriteUUID.toString(), StringUtils.hexString2Bytes(mCurrentCmd), bleWriteCallback);
        }
    }

    public boolean hasScanning() {
        return BleManager.getInstance().getScanSate() == BleScanState.STATE_SCANNING;
    }

    private BleWriteCallback bleWriteCallback = new BleWriteCallback() {
        @Override
        public void onWriteSuccess(int current, int total, byte[] justWrite) {
            Log.d("蓝牙链接", "onWriteSuccess, current:" + current + "," + total + "," + StringUtils.byte2HexStr(justWrite));
        }

        @Override
        public void onWriteFailure(BleException exception) {
//            ToastWrapper.show("写入失败," + exception.getDescription());
            Log.d("蓝牙链接", "onWriteFailure: " + exception.getDescription());
//            EventBus.getDefault().post(new EventBusType<Integer>(EventType.UPDATE_NOTIFY, 2));
        }
    };

    private final BleNotifyCallback mNotifyRsp = new BleNotifyCallback() {
        @Override
        public void onNotifySuccess() {
            GlobeDataManager.bluetoothProfileLiveData.postValue(BluetoothProfile.STATE_CONNECTED);
            GlobeDataManager.bleDeviceData.postValue(mDevice);
        }

        @Override
        public void onNotifyFailure(BleException exception) {
//            ToastWrapper.show("设置监听失败," + exception.getDescription());
            showLog("设置监听失败," + exception.getDescription());
            cancelConnect();
//            EventBusType<String> eventBusScan = new EventBusType<>(EventType.UPDATE_BleScan, "");
//            EventBus.getDefault().post(eventBusScan);
//
//            EventBus.getDefault().post(new EventBusType<Integer>(EventType.UPDATE_NOTIFY, 1));
        }

        @Override
        public void onCharacteristicChanged(byte[] data) {
            String res = StringUtils.byte2HexStr(data); // new String(value, Charset.forName("UTF-8"));
            showLog("接收2： " + res);
            GlobeDataManager.notifyLiveData.postValue(res);
//            mDataParse.appendNewData(res);
        }
    };

    private void connectDeviceIfNeeded() {
        if (!isConnected() && mDevice != null) {
            Log.d("蓝牙链接", "执行了一次");
            connectDevice();
        }
    }

    public boolean isConnected() {
        return getConnectStatus() == BluetoothProfile.STATE_CONNECTED;
    }

    /// 获取连接的状态
    public int getConnectStatus() {
        if (mDevice != null) {
            LocalLogger.i("获取链接状态", "已连接的设备:" + mDevice.getName() + "," + mDevice.getMac());
        }
        return BleManager.getInstance().getConnectState(mDevice);
    }

    private void connectDevice() {
        BleManager.getInstance().connect(mDevice, new BleGattCallback() {
            @Override
            public void onStartConnect() {
                GlobeDataManager.bluetoothProfileLiveData.postValue(BluetoothProfile.STATE_CONNECTING);
            }

            @Override
            public void onConnectFail(BleDevice bleDevice, BleException exception) {
//                ClearUtils.clear();
                GlobeDataManager.bluetoothProfileLiveData.postValue(BluetoothProfile.STATE_DISCONNECTED);
                GlobeDataManager.bleDeviceData.postValue(null);
                if (mHandler != null) {
                    mHandler.removeCallbacksAndMessages(null);
                }
                if (mSyncHandler != null) {
                    mSyncHandler.removeCallbacksAndMessages(null);
                }
            }

            @Override
            public void onConnectSuccess(BleDevice bleDevice, BluetoothGatt gatt, int status) {
                showLog(bleDevice.getMac() + ",状态：" + status);
//                GlobeDataManager.bluetoothProfileLiveData.postValue(BluetoothProfile.STATE_CONNECTED);
                if (gatt == null) {
                    GlobeDataManager.bluetoothProfileLiveData.postValue(BluetoothProfile.STATE_CONNECTED);
                    return;
                }
                requestMTU(gatt);
            }

            @Override
            public void onDisConnected(boolean isActiveDisConnected, BleDevice bleDevice, BluetoothGatt gatt, int status) {
//                ClearUtils.clear();
                GlobeDataManager.bluetoothProfileLiveData.postValue(BluetoothProfile.STATE_DISCONNECTED);
                if (!isActiveDisConnected) {
                    // 被动断开（例如因为连接丢失）
                    showLog("被动断开:");
                } else {
                    // 主动断开
                    showLog("主动断开");
                    mDevice = null;
                }
                if (mHandler != null) {
                    mHandler.removeCallbacksAndMessages(null);
                }
                if (mSyncHandler != null) {
                    mSyncHandler.removeCallbacksAndMessages(null);
                }
//                EventBusType<String> eventBusScan = new EventBusType<>(EventType.UPDATE_BleScan, "");
//                EventBus.getDefault().post(eventBusScan);
            }
        });
    }

    private void requestMTU(BluetoothGatt gatt) {
        BleManager.getInstance().setMtu(mDevice, 512, new BleMtuChangedCallback() {

            @Override
            public void onSetMTUFailure(BleException exception) {
                cancelConnect();
            }

            @Override
            public void onMtuChanged(int mtu) {
                showLog("MTU:" + mtu);

                List<BluetoothGattService> services = gatt.getServices();
                for (int i = 0; i < services.size(); i++) {
                    List<BluetoothGattCharacteristic> characteristics = services.get(i).getCharacteristics();
                    showLog(StrUtil.format("GattService:{}", services.get(i).getUuid()));
                    for (int m = 0; m < characteristics.size(); m++) {
                        BluetoothGattCharacteristic bluetoothGattCharacteristic = characteristics.get(m);
                        if (NOTIFY_UUID.toString().equals(bluetoothGattCharacteristic.getUuid().toString())) {
                            mServiceReadUUID = services.get(i).getUuid();
                            mCharacterReadUUID = characteristics.get(m).getUuid();
                            registerNotify(mServiceReadUUID,mCharacterReadUUID);
                        }
                        if (WRITE_NO_RESPONE_UUID.toString().equals(bluetoothGattCharacteristic.getUuid().toString())) {
                            mServiceWriteUUID = services.get(i).getUuid();
                            mCharacterWriteUUID = characteristics.get(m).getUuid();
                            mSyncHandler.postDelayed(new Runnable() {
                                @Override
                                public void run() {
                                    if (mHandler != null) {
                                        mHandler.post(mRunnable);
                                    }
                                }
                            }, 3 * 1000);
                        }
//                        int properties = bluetoothGattCharacteristic.getProperties();
//                        boolean isWriteOnly = (properties == BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE);
//                        boolean isNotifyOnly = (properties == BluetoothGattCharacteristic.PROPERTY_NOTIFY);
//                        if (isWriteOnly) {
//                            showLog(StrUtil.format("GattCharacteristic: {} supports only write.", bluetoothGattCharacteristic.getUuid()));
//                        }
//                        if (isNotifyOnly) {
//                            showLog(StrUtil.format("GattCharacteristic: {} supports only notify.", bluetoothGattCharacteristic.getUuid()));
//                            registerNotify();
//                        }


//                        }
//                    }
                    }
                }
            }
        });
    }

    public void registerNotify(UUID serviceUUID, UUID readUUID) {
        if (mDevice == null) {
            return;
        }
        BleManager.getInstance().notify(mDevice, serviceUUID.toString(), readUUID.toString(), mNotifyRsp);
    }

    public void registerWrite(UUID serviceUUID, UUID writeUUID, String currentCmd) {
        if (mDevice == null) {
            return;
        }
        BleManager.getInstance().write(mDevice, serviceUUID.toString(), writeUUID.toString(), StringUtils.hexString2Bytes(currentCmd), bleWriteCallback);
    }

    protected void showLog(String log) {
        LocalLogger.i("DataManager------------", log);
    }

    public interface DataChangeListener {

        // 发现新的设备信息
        public void onDeviceFounded(BleDevice bleDevice);

        public void onDeviceFoundStop(List<BleDevice> bleDeviceList);
    }


    private Map<String, DataChangeListener> mDataChangeListenerList = new HashMap<String, DataChangeListener>();

    public BleTemplate addDataChangeListener(String key, DataChangeListener listener) {
        if (key == null || listener == null) return this;
        mDataChangeListenerList.put(key, listener);
        return this;
    }

    public void removeDataChangeListener(String key) {
        if (key == null) return;
        mDataChangeListenerList.remove(key);
    }

    // 开始扫描
    public void searchDevice() {
        BleScanRuleConfig scanRuleConfig = new BleScanRuleConfig.Builder()
//                .setServiceUuids(new UUID[]{Config.SERVICE_UUID})      // 只扫描指定的服务的设备，可选
                .setAutoConnect(false)      // 连接时的autoConnect参数，可选，默认false
                .setScanTimeOut(15 * 1000)              // 扫描超时时间，可选，默认10秒
                .build();
        BleManager.getInstance().initScanRule(scanRuleConfig);
        BleManager.getInstance().scan(mSearchResponse);
    }
    public void stopSearch() {
       if(isScan()){
           BleManager.getInstance().cancelScan();
       }
    }

    private final BleScanCallback mSearchResponse = new BleScanCallback() {
        @Override
        public void onScanFinished(List<BleDevice> scanResultList) {
            isScan = false;
            if (mDataChangeListenerList != null && mDataChangeListenerList.size() > 0) {
                for (DataChangeListener listener : mDataChangeListenerList.values()) {
                    if (listener != null) listener.onDeviceFoundStop(scanResultList);
                }
            }
        }

        @Override
        public void onScanStarted(boolean success) {
            isScan = true;
        }

        @Override
        public void onScanning(BleDevice bleDevice) {
            addNewDevices(bleDevice);
        }
    };

    public boolean isScan() {
        return isScan;
    }

    public UUID getmServiceReadUUID() {
        return mServiceReadUUID;
    }

    public UUID getmCharacterReadUUID() {
        return mCharacterReadUUID;
    }

    public UUID getmServiceWriteUUID() {
        return mServiceWriteUUID;
    }

    public UUID getmCharacterWriteUUID() {
        return mCharacterWriteUUID;
    }

    public BleDevice getDevice() {
        return mDevice;
    }

    @SuppressLint("SuspiciousIndentation")
    private void addNewDevices(BleDevice device) {
        if (device == null || StrUtil.isEmpty(device.getName())) return;
//        if(device.getName().startsWith("BCL603")){
//        if(BleNameFilter.BleScanName.hasExits(device.getName())){
//            LocalLogger.i("蓝牙扫描", device.getName()+"~"+BleNameFilter.BleScanName.hasExits(device.getName()));
//        showLog("addNewDevices: "+device.getMac());
        if (mDataChangeListenerList != null && mDataChangeListenerList.size() > 0) {
            for (DataChangeListener listener : mDataChangeListenerList.values()) {
                if (listener != null) listener.onDeviceFounded(device);
            }
        }
//        }
    }
}
